﻿using System;
using System.Diagnostics;
using System.Reflection;
using System.Linq;
using System.Threading;
using Fine.Collections.Const;
using Fine.Collections.Cont;
using Fine.PerformanceTestUtils;
using JetBrains.Annotations;



namespace Fine
{

    class PerformanceTestProgram
    {

        const int performCount = 10 ;


        static void Main(string[] args)
        {
            Thread.Sleep(300);
            RunClassTests(typeof(IteratorsCompareTest));
            RunClassTests(typeof(ListsCompareTest));
        }


        private static void RunClassTests(Type clazz)
        {
            var methods =
                clazz.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public |
                                 BindingFlags.NonPublic | BindingFlags.Instance)
                     .Where(m => !m.IsPrivate)
                     .Where(m => m.GetCustomAttributes(typeof(PerfTestAttribute),false).Length > 0)
                     .ToConstList();
            if (methods.IsEmpty)
                return;

            var testObjectConstructor = clazz.GetConstructor(noTypes);
            var testObject = testObjectConstructor.Invoke(noObjects);

            Thread.Sleep(100);

            foreach (var method in methods)
            {
                double time;
                RunOneTestSeveralTimes(testObject, method, out time);
                Console.Out.WriteLine("{0}: {1:##0.000000}", method.Name, time);
            }
        }


        private static void RunOneTestSeveralTimes ([NotNull] object testObject, 
                                                   [NotNull] MethodInfo testMethod,
                                                   out double avgTime)
        {
            double sumTime = 0;

            for (int k = 1; k <= performCount; k++)
            {
                double oneTime;
                RunOneTest(testObject, testMethod, out oneTime);
                sumTime += oneTime;
            }

            avgTime = sumTime/performCount;
        }



        private static void RunOneTest([NotNull] object testObject, 
                                       [NotNull] MethodInfo testMethod,
                                       out double time)
        {
            var watch = new Stopwatch();

            Thread.Sleep(50);

            watch.Start();
            try
            {
                testMethod.Invoke(testObject, noObjects);
            }
            finally
            {
                watch.Stop();
            }

            time = 1.0 * watch.ElapsedTicks / Stopwatch.Frequency;
        }


        private static readonly Type[] noTypes = new Type[0];
        private static readonly object[] noObjects = new object[0];
    }
}
